package conf

import (
	"context"
	"database/sql"
	"fmt"
	"gitee.com/hexug/vblog/tree/master/api/apps/user"
	_ "github.com/go-sql-driver/mysql"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"sync"
	"time"
)

// Server 服务部分的结构体
type Server struct {
	//主机ip或者主机名
	Host string `toml:"host" env:"SERVER_HOST"`
	//暴露的端口
	Port int `toml:"port" env:"SERVER_PORT"`
}

// Auth 认证相关的结构体
type Auth struct {
	//用户名
	UserName string `env:"AUTH_USERNAME" toml:"username" `
	//密码
	PassWord string `env:"AUTH_PASSWORD" toml:"password" `
}

// CheckAuth 加上验证用户名和密码的方法
func (a *Auth) CheckAuth(request *user.LoginRequest) bool {
	if request.UserName == a.UserName && request.PassWord == a.PassWord {
		return true
	}
	return false
}

// Mysql 数据库相关的配置
type Mysql struct {
	UserName string `env:"MYSQL_USERNAME" toml:"username" `
	PassWord string `env:"MYSQL_PASSWORD" toml:"password" `
	Host     string `toml:"host" env:"DB_HOST"`
	Port     int    `toml:"port" env:"DB_PORT"`
	//数据库
	Database string `toml:"database" env:"DATABASE"`
	//编码
	Charset   string `toml:"charset" env:"DB_CHARSET"`
	ParseTime string `toml:"parseTime" env:"DB_PARSE_TIME"`
	//是否可以执行多条sql语句
	MultiStateMents string `toml:"multiStatements" env:"MYSQL_MULTISTATEMENTS"`
	//最大连接数
	MaxOpenConn int `toml:"maxopenconn" env:"MYSQL_MAXOPENCONN"`
	//最大空闲数
	MaxIdleconn int `toml:"maxidleconn" env:"MYSQL_MAXIDLECONN"`

	//高级配置
	ConnMaxLifeTime int `toml:"connmaxlifetime" env:"MYSQL_CONNMAXLIFETIME"`
	ConnMaxIdleTime int `toml:"connmaxidletime" env:"MYSQL_CONNMAXIDLETIME"`
	TimeOut         int `toml:"timeout" env:"MYSQL_TIMEOUT"`

	db   *gorm.DB
	lock sync.Mutex
}

// 日志的相关配置
type Log struct {
	//文件保存的文件夹
	SaveFileDir string `toml:"savefiledir" env:"LOG_SAVEFILEDIR"`
	//是否保存在文件中
	SaveFile bool `toml:"savefile" env:"LOG_SAVEFILE"`
	//日志的等级 这里指的是控制台打印的日志
	LogLevel string `toml:"loglevel" env:"LOG_LEVEL"`
}

type Conf struct {
	*Auth   `toml:"auth"`
	*Mysql  `toml:"mysql"`
	*Log    `toml:"log"`
	*Server `toml:"server"`
}

//下面都是默认配置

func DefaultServer() *Server {
	return &Server{
		Host: "127.0.0.1",
		Port: 8080,
	}
}

func DefaultAuth() *Auth {
	return &Auth{
		UserName: "defaultadmin",
		PassWord: "defaultadmin",
	}
}

func DefaultMysql() *Mysql {
	return &Mysql{
		UserName:        "admin",
		PassWord:        "111111",
		Host:            "127.0.0.1",
		Port:            3306,
		Database:        "test",
		Charset:         "utf8mb4",
		ParseTime:       "True",
		MultiStateMents: "True",
		MaxOpenConn:     50,
		MaxIdleconn:     10,
		TimeOut:         5,
	}
}

func DefaultLog() *Log {
	return &Log{
		LogLevel: "debug",
		SaveFile: false,
	}
}

// 利用原生sql创建连接池
func (m *Mysql) GetConnPool() (*sql.DB, error) {
	//创建dsn
	// multiStatements 让db 可以执行多个语句 select; insert;
	dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s&parseTime=%s&loc=Local&multiStatements=%s", m.UserName, m.PassWord, m.Host, m.Port, m.Database, m.Charset, m.ParseTime, m.MultiStateMents)
	//创建原生db链接
	db, err := sql.Open("mysql", dsn)
	if err != nil {
		return nil, err
	}

	// 对连接池进行设置
	db.SetMaxOpenConns(m.MaxOpenConn) //最大连接数
	db.SetMaxIdleConns(m.MaxIdleconn) //最大空闲数
	//设置可以重复使用连接的最大时间，过期的连接可以在重用之前延迟关闭。
	if m.ConnMaxLifeTime > 0 {
		db.SetConnMaxLifetime(time.Second * time.Duration(m.ConnMaxLifeTime)) //如果设置的时间小于等于0，则连接不会因连接老化而关闭。
	}
	// 设置连接可能空闲的最长时间。过期的连接可以在重新使用之前延迟关闭。
	if m.ConnMaxIdleTime > 0 {
		db.SetConnMaxIdleTime(time.Second * time.Duration(m.ConnMaxIdleTime)) //如果设置的时间小于等于0，则不会因为连接在空闲时间就关闭。
	}

	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(m.TimeOut)*time.Second)
	defer cancel()
	//先ping一下数据库，看下是否存活
	if err := db.PingContext(ctx); err != nil {
		return nil, err
	}
	return db, nil
}

// 然后利用连接池再创建响应的db
func (m *Mysql) ORM() *gorm.DB {
	if m.db == nil {
		m.lock.Lock()
		defer m.lock.Unlock()
		db, err := m.GetConnPool()
		if err != nil {
			panic(err)
		}
		//定义时间精度的变量
		var datetimePrecision = 2
		gormdb, err := gorm.Open(
			mysql.New(
				mysql.Config{
					Conn:                     db,
					DefaultDatetimePrecision: &datetimePrecision, //默认时间精度
				}), &gorm.Config{
				// 执行任何 SQL 时都创建并缓存预编译语句，可以提高后续的调用速度
				PrepareStmt: true,

				// 对于写操作（创建、更新、删除），为了确保数据的完整性，GORM 会将它们封装在事务内运行。
				// 但这会降低性能，如果没有这方面的要求，您可以在初始化时禁用它，这将获得大约 30%+ 性能提升
				SkipDefaultTransaction: true,

				// 要有效地插入大量记录，请将一个 slice 传递给 Create 方法、可以批量创建
				// CreateBatchSize: 200,
			})
		if err != nil {
			panic(err)
		}
		m.db = gormdb
	}
	return m.db
}
